/*
 * Copyright 2007. Mount Sinai Hospital, Toronto, Canada.
 * 
 * Licensed under the GNU Lesser General Public License, Version 2. You
 * can find a copy of the license at:
 * 
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 * 
 * IN NO EVENT SHALL MOUNT SINAI HOSPITAL BE LIABLE TO ANY PARTY FOR DIRECT, 
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 
 * PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 
 * EVEN IF MOUNT SINAI HOSPITAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 * MOUNT SINAI HOSPITAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND 
 * ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". 
 * MOUNT SINAI HOSPITAL HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, 
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 
 */
package com.sinai.mshab.client.ui.basic;

import com.google.gwt.user.client.ui.CellPanel;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sinai.mshab.client.util.Answer;
import com.sinai.mshab.client.ui.FormComponent;
import com.sinai.mshab.client.ui.aggregate.DataReceiver;
import com.sinai.mshab.client.ui.filter.BasicFilter;

/**
 * <code>BasicFormComponent</code> offers another layer between basic
 * components and {@link FormComponent}. This allows for the creation of basic
 * component specific methods that are enforced in all basic components, without
 * forcing the methods on aggregate components.
 * 
 * @author Arthur Kalmenson
 */
public abstract class BasicFormComponent extends FormComponent implements
		BasicFilter {

	/**
	 * The CSS style name for the small helper text.
	 */
	private static final String HELPER_TEXT_SMALL_STYLE = "mshab-helperTextSmall";

	/**
	 * The CSS style name for the large helper text.
	 */
	private static final String HELPER_TEXT_LARGE_STYLE = "mshab-helperTextLarge";

	/**
	 * The CSS style name for the link that reveals the helper text.
	 */
	private static final String HELPER_TEXT_LINK_STYLE = "mshab-helperTextLink";

	/**
	 * The CSS style name given to the widget's text when toReport is called.
	 */
	public static final String ANSWER_LABEL_STYLE = "mshab-reportWidgetText";

	/**
	 * The CSS style name given to the description label of widgets that are
	 * aligned vertically.
	 */
	private static final String DESCRIPTION_LABEL_VERTICAL_STYLE = "mshab-descriptionLabelVertical";

	/**
	 * The CSS style name given to the quantifier label of widgets that are
	 * aligned vertically.
	 */
	private static final String QUANTIFIER_LABEL_VERTICAL_STYLE = "mshab-quantifierLabelVertical";

	/**
	 * A description {@link Label} which appears before the widget.
	 */
	protected Label description_label;

	/**
	 * A quantifier {@link Label} which appears after the widget.
	 */
	protected Label quantifier_label;

	/**
	 * An error {@link Label} which appears after the quantifier and only
	 * appears when the component is in an error state, i.e. a validator fails.
	 */
	protected Label error_label;

	/**
	 * The widget where form users input their information.
	 */
	protected Widget widget;

	/**
	 * The text that appears in small helper texts.
	 */
	protected String helperText;

	/**
	 * Whether this BasicFormComponent is horizontal or vertical.
	 */
	protected boolean horizontal;

	/**
	 * The helper {@link Widget} that is displayed when the user requires more
	 * information in the large helper text.
	 */
	protected Widget helperWidget;

	/**
	 * The panel where all the various components live, i.e. the description,
	 * quantifier and error labels as well as the widget itself.
	 */
	protected CellPanel panel;

	public void initialize(String dbfieldName, String tableName,
			String description, String quantifier, String error_msg, Widget w,
			boolean horizontal) {
		this.dbfieldName = dbfieldName;
		this.tableName = tableName;
		this.description = description;
		this.quantifier = quantifier;
		this.error_msg = error_msg;
		this.horizontal = horizontal;

		// all our widgets go to the horizontal panel and add custom style names
		// for CSS to give custom rendering to these components
		if (description != null) {
			description_label = new Label(description);

			// if it's horizontal, use the regular style.
			if (horizontal) {
				description_label.setStyleName("description_label");
			} else {

				// otherwise specify it's vertical.
				description_label
						.setStyleName(DESCRIPTION_LABEL_VERTICAL_STYLE);
			}
		}
		if (w != null) {
			widget = w;

			// set the proper alt
			widget.setTitle(description);
		}
		if (quantifier != null) {
			quantifier_label = new Label(quantifier);

			// if it's horizontal, use the regular style.
			if (horizontal) {
				quantifier_label.setStyleName("quantifier_label");
			} else {

				// otherwise specify it's vertical.
				quantifier_label.setStyleName(QUANTIFIER_LABEL_VERTICAL_STYLE);
			}
		}
		if (error_msg != null) {
			error_label = new Label(error_msg);
			error_label.setStyleName("error_label");

			// no error at creation time, so error label not visible.
			error_label.setVisible(false);
		}

		// the panel being used will depend on horizontal.
		if (horizontal) {
			panel = new HorizontalPanel();

			// panel containing other widgets
			if (description_label != null) {
				panel.add(description_label);
			}
			if (widget != null) {
				panel.add(widget);
			}
			if (quantifier_label != null) {
				panel.add(quantifier_label);
			}
			if (error_label != null) {
				panel.add(error_label);
			}
		} else {
			panel = new VerticalPanel();

			// panel containing other widgets
			if (description_label != null) {
				panel.add(description_label);
			}
			if (quantifier_label != null) {
				panel.add(quantifier_label);
			}
			if (error_label != null) {
				panel.add(error_label);
			}
			if (widget != null) {
				panel.add(widget);
			}
		}

		// starting the panel.
		initWidget(panel);
	}

	public abstract Object clone();

	public void sendAnswer(DataReceiver receiver) {
		// TODO Auto-generated method stub

	}

	/**
	 * Returns a printable version of this <code>BasicFormComponent</code>.
	 * The {@link HorizontalPanel} is returned in the following format:
	 * <p>
	 * description Label | user's input in a Label | quantifier Label
	 * </p>
	 * 
	 * @return a printable version of this <code>BasicFormComponent</code>.
	 */
	public abstract HorizontalPanel toReport();

	/**
	 * Since all <code>BasicFormComponent</code>s have a very similar
	 * <code>toPrint()</code>, this helper method was created. The String,
	 * <code>answer</code>, is the user inputted data in String format. This
	 * is added to the known description and quantifier labels to make the
	 * correct {@link HorizontalPanel}.
	 * 
	 * @param answer
	 *            the user inputted data in String form.
	 * @return a printable version of this <code>BasicFormComponent</code>.
	 * 
	 * @see BasicFormComponent#toReport()
	 */
	protected HorizontalPanel toPrint(String answer) {
		HorizontalPanel printableVersion = new HorizontalPanel();

		// add the description label.
		if (description_label != null) {

			// make a copy of the label.
			Label description = new Label(description_label.getText());
			description.setStyleName(description_label.getStyleName());
			printableVersion.add(description);
		}

		// add a new label with the answer.
		if (answer != null) {

			Label answerLabel = new Label(answer);
			answerLabel.setStyleName(ANSWER_LABEL_STYLE);
			printableVersion.add(answerLabel);
		}

		// add the quantifier.
		if (quantifier_label != null) {

			// make a copy of the label.
			Label quantifier = new Label(quantifier_label.getText());
			quantifier.setStyleName(quantifier_label.getStyleName());
			printableVersion.add(quantifier);
		}

		return printableVersion;
	}

	/**
	 * Returns the text associated with this component.
	 * 
	 * @return the text associated with this component.
	 */
	public String getDescription() {
		if (description_label == null)
			return "";
		return description_label.getText();
	}

	public String getColumn() {
		return this.dbfieldName;
	}

	public String getOperator() {
		return "=";
	}

	public String getValue() {
		Answer a = getAnswer();
		if (a == null) {
			return "";
		}
		return a.getFirstAnswer();
	}

	/**
	 * Returns the internal widget of this BasicFormComponent. For example, if
	 * we have a TextAreaComponent, this method would return the
	 * {@link TextArea} contained in the component.
	 * 
	 * @return the internal widget of this BasicFormComponent.
	 */
	public Widget getInternalWidget() {
		return widget;
	}

	/**
	 * Attaches helper text to this component. Helper text is similar to a
	 * quantifier in the sense that it assists a user in filling in a particular
	 * field. However, helper text differs in that it appears in a
	 * {@link PopupPanel} and its primary purpose is for large amounts of text.
	 * 
	 * @param displayText
	 *            the text that appears on the link that is to be clicked to
	 *            reveal helperText.
	 * @param helperText
	 *            the text that appears when displayText is clicked.
	 */
	public void setHelperTextSmall(String displayText, String helperText) {
		this.helperText = helperText;

		// make it a disclosure panel.

		// the label that is displayed
		Label helperLink = new Label(displayText);
		helperLink.setStyleName(HELPER_TEXT_LINK_STYLE);
		DisclosurePanel helper = new DisclosurePanel(helperLink);

		// the label that is disclosed.
		Label helperLabel = new Label(helperText);
		helperLabel.setStyleName(HELPER_TEXT_SMALL_STYLE);
		helper.add(helperLabel);

		// add the clickable link to before the widget. So first we find where
		// the widget is.
		for (int i = 0; i < panel.getWidgetCount(); i++) {

			// if the widget is not a label, that means we've found where the
			// widget lives, so we insert the helper text before it.
			if (!(panel.getWidget(i) instanceof Label)) {

				insertIntoPanel(helper, i);
			}
		}
	}

	/**
	 * Attaches helper text to this component. Helper text is similar to a
	 * quantifier in the sense that it assists a user in filling in a particular
	 * field. However, helper text differs in that it appears in a
	 * {@link PopupPanel} and its primary purpose is for large amounts of text.
	 * 
	 * @param popupText
	 *            the text that appears on the link that is to be clicked to
	 *            reveal helperText.
	 * @param helperWidget
	 *            the helper, a {@link Widget}, that appears when popupText is
	 *            clicked.
	 */
	public void setHelperTextLarge(String popupText, Widget helperWidget) {

		// set up the clickable link.
		final Label popupDisplayer = new Label(popupText);

		// give it a style.
		popupDisplayer.setStyleName(HELPER_TEXT_LINK_STYLE);

		// set up the helper text that is to appear and give it a style.
		this.helperWidget = helperWidget;
		this.helperWidget.setStyleName(HELPER_TEXT_LARGE_STYLE);

		// add the listener to the clickable link so when it is clicked, the
		// popupPanel will appear.
		popupDisplayer.addClickListener(new ClickListener() {
			public void onClick(Widget sender) {

				// make a new HelperText popup and make it popup near the link
				// that was clicked. This was figured out using Google's Mail
				// app, so kudos to them :)
				HelperTextPopup text = new HelperTextPopup();
				int left = popupDisplayer.getAbsoluteLeft() + 15;
				int top = popupDisplayer.getAbsoluteTop() + 15;
				text.setPopupPosition(left, top);
				text.show();
			}
		});

		// add the clickable link to before the widget. So first we find where
		// the widget is.
		for (int i = 0; i < panel.getWidgetCount(); i++) {

			// if the widget is not a label, that means we've found where the
			// widget lives, so we insert the helper text before it.
			if (!(panel.getWidget(i) instanceof Label)
					&& !(panel.getWidget(i) instanceof HelperTextPopup)) {

				insertIntoPanel(popupDisplayer, i);
			}
		}
	}

	/**
	 * Inserts w at index, index, into the panel that this BasicFormComponent is
	 * in (i.e. all the panel that holds the description label, quantifier
	 * label, etc).
	 * 
	 * @param w
	 *            the widget to insert.
	 * @param index
	 *            where to insert w.
	 */
	private void insertIntoPanel(Widget w, int index) {

		// unfortunately CellPanels don't have an insert method, so I
		// have to do this nasty code.
		if (panel instanceof HorizontalPanel) {
			((HorizontalPanel) panel).insert(w, index);
		} else if (panel instanceof VerticalPanel) {
			((VerticalPanel) panel).insert(w, index);
		}
	}

	/**
	 * Returns the helper text assigned to this widget.
	 * 
	 * @return the helper text assigned to this widget.
	 */
	public String getHelperTextSmall() {
		return helperText;
	}

	/**
	 * Returns the widget that is displayed for this widget's large helper text.
	 * 
	 * @return the widget that is displayed for this widget's large helper text.
	 */
	public Widget getHelperTextLarge() {
		return helperWidget;
	}

	/**
	 * The helper text {@link PopupPanel} that will appear when the user
	 * requests helper text for a specific mshab widget.
	 * 
	 * @author Arthur Kalmenson.
	 */
	private class HelperTextPopup extends PopupPanel {
		public HelperTextPopup() {
			super(true);

			// give it some width and height so it's readable.
			setWidth("600px");
			setHeight("350px");

			// add the widget.
			setWidget(helperWidget);
		}
	}

	/**
	 * Sets this question as a subquestion. This sets the description label's
	 * style to "sub_description_label".
	 */
	public void setAsSubQuestion() {
		if (description_label != null) {
			subquestion = true;
			description_label.setStyleName("sub_description_label");
		}
	}
}
